package handler

import (
	"net/http"

	"database/sql"

	"gitee.com/hp-huiw/family-expense-api/internal/dto"
	"gitee.com/hp-huiw/family-expense-api/internal/service"
	"gitee.com/hp-huiw/my-go-core/date"
	"gitee.com/hp-huiw/my-go-core/util"
	"github.com/gin-gonic/gin"
)

type IncomeExpenseDetailHandler interface {
	QueryListWithSummaryByAccountGroup(ctx *gin.Context)
	QueryListByAccountGroup(ctx *gin.Context)
	QueryTotalAmountByAccountGroup(ctx *gin.Context)
	QueryDetails(ctx *gin.Context)
	QueryCreditCardBill(ctx *gin.Context)
	QueryCreditCardBillAmount(ctx *gin.Context)
	QueryCoordinates(ctx *gin.Context)
	Add(ctx *gin.Context)
	Update(ctx *gin.Context)
	Delete(ctx *gin.Context)
}

func NewIncomeExpenseDetailHandler(db *sql.DB) IncomeExpenseDetailHandler {
	return &incomeExpenseDetailHandler{
		incomeExpenseDetailService: service.NewIncomeExpenseDetailService(db),
	}
}

// Matches /query-list-with-summary-by-account-group of http get action.
func (h *incomeExpenseDetailHandler) QueryListWithSummaryByAccountGroup(ctx *gin.Context) {
	accountGroupID := GetClaims(ctx).AccountGroupID
	var params dto.IncomeExpenseDetailQueryArgumentRequest

	if err := ctx.ShouldBindQuery(&params); err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	result, err := h.incomeExpenseDetailService.QueryListWithSummary(c, accountGroupID, params.PageSize, params.PageIndex)

	if err != nil {
		ctx.Error(err)
		return
	}

	SendDataResponse(ctx, result)
}

// Matches /query-list-by-account-group of http get action.
func (h *incomeExpenseDetailHandler) QueryListByAccountGroup(ctx *gin.Context) {
	var params dto.IncomeExpenseDetailQueryArgumentRequest

	if err := ctx.ShouldBindQuery(&params); err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	params.Init()
	params.AccountGroupID = GetClaims(ctx).AccountGroupID
	result, err := h.incomeExpenseDetailService.Query(c, &params)

	if err != nil {
		ctx.Error(err)
		return
	}

	SendDataResponse(ctx, result)
}

// Matches /query-total-amount-by-account-group of http get action.
func (h *incomeExpenseDetailHandler) QueryTotalAmountByAccountGroup(ctx *gin.Context) {
	accountGroupID := GetClaims(ctx).AccountGroupID
	var params dto.IncomeExpenseDetailQueryArgumentRequest

	if err := ctx.ShouldBindQuery(&params); err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	params.Init()
	result, err := h.incomeExpenseDetailService.QueryTotalAmount(c, accountGroupID, &params)

	if err != nil {
		ctx.Error(err)
		return
	}

	SendDataResponse(ctx, result)
}

// Matches /query-details of http get action.
func (h *incomeExpenseDetailHandler) QueryDetails(ctx *gin.Context) {
	c := ctx.Request.Context()
	accountGroupID := GetClaims(ctx).AccountGroupID
	id := ctx.Query("id")
	result, err := h.incomeExpenseDetailService.QueryDetails(c, accountGroupID, id)

	if err != nil {
		ctx.Error(err)
		return
	}

	SendDataResponse(ctx, result)
}

// Matches /query-credit-card-bill of http get action.
func (h *incomeExpenseDetailHandler) QueryCreditCardBill(ctx *gin.Context) {
	accountGroupID := GetClaims(ctx).AccountGroupID
	pageSize, err := util.StringToInt64(ctx.Query("pageSize"))

	if err != nil {
		ctx.Error(err)
		return
	}

	pageIndex, err := util.StringToInt64(ctx.Query("pageIndex"))

	if err != nil {
		ctx.Error(err)
		return
	}

	bankCardID := ctx.Query("bankCardId")
	statementDate, err := date.ParseFromLongDateTime(ctx.Query("statementDate"))

	if err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	result, err := h.incomeExpenseDetailService.QueryCreditCardBill(c, accountGroupID, pageSize, pageIndex, bankCardID, statementDate)

	if err != nil {
		ctx.Error(err)
		return
	}

	SendDataResponse(ctx, result)
}

// Matches /query-credit-card-bill-amount of http get action.
func (h *incomeExpenseDetailHandler) QueryCreditCardBillAmount(ctx *gin.Context) {
	accountGroupID := GetClaims(ctx).AccountGroupID
	bankCardID := ctx.Query("bankCardId")
	statementDate, err := date.ParseFromLongDateTime(ctx.Query("statementDate"))

	if err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	result, err := h.incomeExpenseDetailService.QueryCreditCardBillAmount(c, accountGroupID, bankCardID, statementDate)

	if err != nil {
		ctx.Error(err)
		return
	}

	SendDataResponse(ctx, result)
}

// Matches /query-coordinates of http get action.
func (h *incomeExpenseDetailHandler) QueryCoordinates(ctx *gin.Context) {
	c := ctx.Request.Context()
	accountGroupID := GetClaims(ctx).AccountGroupID
	year := ctx.Query("year")
	result, err := h.incomeExpenseDetailService.QueryCoordinates(c, accountGroupID, year)

	if err != nil {
		ctx.Error(err)
		return
	}

	SendDataResponse(ctx, result)
}

// Matches /add of http post action.
func (h *incomeExpenseDetailHandler) Add(ctx *gin.Context) {
	accountGroupID := GetClaims(ctx).AccountGroupID
	var postedData dto.IncomeExpenseDetailRequest

	if err := ctx.ShouldBindJSON(&postedData); err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	claims := GetClaims(ctx)

	if err := h.incomeExpenseDetailService.Add(c, &postedData, accountGroupID, claims.Username); err != nil {
		ctx.Error(err)
		return
	}

	SendStatusResponse(ctx, http.StatusNoContent)
}

// Matches /update of http post action.
func (h *incomeExpenseDetailHandler) Update(ctx *gin.Context) {
	postedData := &dto.IncomeExpenseDetailRequest{}

	if err := ctx.ShouldBindJSON(&postedData); err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	claims := GetClaims(ctx)

	if err := h.incomeExpenseDetailService.Update(c, claims.AccountGroupID, claims.Username, postedData); err != nil {
		ctx.Error(err)
		return
	}

	SendStatusResponse(ctx, http.StatusNoContent)
}

// Matches /delete of http post action.
func (h *incomeExpenseDetailHandler) Delete(ctx *gin.Context) {
	var postedData struct {
		ID string `json:"id"`
	}

	if err := ctx.ShouldBindJSON(&postedData); err != nil {
		ctx.Error(err)
		return
	}

	c := ctx.Request.Context()
	accountGroupID := GetClaims(ctx).AccountGroupID

	if err := h.incomeExpenseDetailService.Delete(c, accountGroupID, []string{postedData.ID}); err != nil {
		ctx.Error(err)
		return
	}

	SendStatusResponse(ctx, http.StatusNoContent)
}

// Container of IncomeExpenseDetailHandler functions.
type incomeExpenseDetailHandler struct {
	incomeExpenseDetailService service.IncomeExpenseDetailService
}
